package cn.nju.seg.atg.parse;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.model.IFunctionDeclaration;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;

import cn.nju.seg.atg.GA.GA_for_ATG;
import cn.nju.seg.atg.callCPP.CallCPP;
import cn.nju.seg.atg.util.CFGPath;
import cn.nju.seg.atg.util.PathUtil;
import cn.nju.seg.atg.visitor.CFGNode;
import cn.nju.seg.atg.visitor.FunctionVisitor;

/**
 * 
 * @author ChengXin
 * 
 */
@SuppressWarnings("deprecation")
public class Builder extends AbstractAST {
	
	/**
	 * 需要调用的action的名字
	 */
	private String actionName;

	/**
	 * 执行单元测试的函数名
	 */
	public static String funcName = null;
	
	/**
	 * 由函数转成的CFG树的开始节点
	 */
	public static CFGNode cfgStartNode = null;
	
	/**
	 * CFG树中的当前节点，是全局变量，在构建、遍历CFG树的时候会用到
	 */
	public static CFGNode currentNode = null;
	
	/**
	 * CFG树中节点的编号，是全局变量，构建CFG树时会用到
	 */
	public static int nodeNumber = -1;

	/**
	 * 构建CFG树过程中用于存放终止节点的链表，是全局变量
	 */
	public static List<CFGNode> terminalNodes = new ArrayList<CFGNode>();

	/**
	 * 用于存放已打印节点的链表，CFG树输出是会用到
	 */
	public static List<Integer> nodePrinted = new ArrayList<Integer>();

	/**
	 * 当前执行单元测试的函数名
	 */
	public static String currentFunctionName = null;

	/**
	 * 自变参数产生池
	 */
	public static List<Double> parameterList = new LinkedList<Double>();
	
	/**
	 * 目标路径
	 */
	public static CFGPath targetPath = new CFGPath();
	
	/**
	 * 当前输入向量(动态方法时使用)
	 */
//	public static double[] parameters;
	
	/**
	 * 最优输入参数（静态方法时使用)
	 */
	public static double[] optimalParams;
	
	/**
	 * 当前Eclispe实例的console流
	 */
	public static MessageConsoleStream consoleStream;
	
	/**
	 * 执行待测cpp程序的对象
	 */
	public static CallCPP callCPP;
	
	/**
	 * 向两边扩展后得到的新自变参数
	 */
	public static List<Double> autoIncreasedParameterList;
	
	/**
	 * 目标程序执行的总时间、执行次数,遗传算法试验次数
	 */
	public static double function_time;
	public static int function_frequency;
	public static int GA_trials;
	/**
	 * 尚未做ATG的路径集合
	 */
	public static List<CFGPath> uncheckedPaths;
	
	/**
	 * 执行过ATG后仍未找到可用输入向量的路径集合
	 */
	public static List<CFGPath> uncoveredPaths;
	
	/**
	 * 执行过ATG后已找到可用输入向量的路径集合
	 */
	public static List<CFGPath> coveredPaths;
	
	/**
	 * 带测程序的所有路径
	 */
	public static List<CFGPath> allPaths;
	
	/**
	 * 程序结束后，被覆盖到的路径数
	 */
	public static int countOfCoveredPath = 0;
	
	/**
	 * 被覆盖到的路径编号集合
	 */
	public static String strCoveredPath = "";
	
	/**
	 * 函数输入参数的类型
	 */
	public static String[] parameterTypes = null;	
	
	/**
	 * 带参构造函数
	 * @param actionName
	 */
	public Builder(String actionName)
	{
		this.actionName = actionName;
	}
	
	@Override
	public void run(IFunctionDeclaration ifd) {
		
/************************************************
 | 获取当前"测试Eclipse"的Console窗口				|
 | 将其命名为"CLF Console"，为输出运行结果做准备		|
 ***********************************************/
		//定义一个console窗口
		MessageConsole console = new MessageConsole("ATG Console",null);
		//将console窗口添加到当前测试Eclipse中
		ConsolePlugin.getDefault().getConsoleManager().addConsoles(new IConsole[]{console});
		//获取console的信息流consoleStream，为输出运行结果做准备
		consoleStream = console.newMessageStream();
		//在当前测试Eclipse中显示该console的内容
		ConsolePlugin.getDefault().getConsoleManager().showConsoleView(console);
		
		
/************************************************
 | 借助CDT插件，遍历函数节点ifd，生成CFG树			|
 ***********************************************/
		IASTTranslationUnit iatu = parse(ifd);
		cfgStartNode = new CFGNode();
		currentNode = new CFGNode();
		nodeNumber = 0;
		FunctionVisitor expressionVisitor = new FunctionVisitor();
		this.setParsed(expressionVisitor, iatu);

		
/************************************************
 | 从CFG树的条件节点中取出逻辑符号					|
 | 并记录到该条件节点中                         	|
 ***********************************************/
		nodePrinted = new ArrayList<Integer>();
		addSymbol(cfgStartNode);
		
		
/************************************************
 | 2012.10.24								    |
 | 利用zest插件，显示CFG的树形结构			    	|
 ***********************************************/
		if (this.actionName.equals("cfg"))
		{
			Builder.function_time = 0;
			
			ShowCFG showcfg = new ShowCFG(cfgStartNode);
			showcfg.showCFG();
		}
		
		
/************************************************
 | 生成所有待测试路径								|
 | 对每一条路径做测试数据自动生成			    	|
 ***********************************************/
		if (this.actionName.equals("atg"))
		{
			// 初始化目标程序执行时间为0
			Builder.function_time = 0;
			// 初始化目标程序执行次数为0
			Builder.function_frequency = 0;
			Builder.GA_trials = 0;
			
			Builder.callCPP = new CallCPP();
			
			// 初始化已被覆盖的路径数为0
			Builder.countOfCoveredPath = 0;
			// 初始化已被覆盖的路径集合为空
			Builder.strCoveredPath = "";
			// 判断当前路径是否被覆盖：-1:未覆盖；0~+:被覆盖；
			int isCovered = -1;

			// 初始化未执行过ATG的路径集合
			Builder.uncheckedPaths = PathUtil.getAllPaths(cfgStartNode);
/*			
			Builder.allPaths = new ArrayList<CFGPath>();
			int pathsSize = Builder.uncheckedPaths.size();
			for (int i=0; i<pathsSize; i++)
			{
				Builder.allPaths.add(Builder.uncheckedPaths.get(i).clonePath());
			}
		
			Iterator<CFGPath> itr1 = uncheckedPaths.iterator();
			while(itr1.hasNext()){
				CFGPath nextPath = itr1.next();
				System.out.println("为第"+Builder.getPathNum(nextPath)+"条路径生成测试数据:");
			Iterator<SimpleCFGNode> itr = nextPath.getPath().iterator();
			while(itr.hasNext()){
				SimpleCFGNode nextNode = itr.next();
				System.out.println("CFGNode:"+nextNode.getName().toString());
				if(nextNode.isBranchNode()){
					//System.out.println("value:"+nextNode.getValue());
				    System.out.println("isTrue:"+nextNode.isTrue());		    
				}
			}
			}
*/
			
			// 初始化未找到可用输入向量的集合为空
			Builder.uncoveredPaths = new ArrayList<CFGPath>();
			// 初始化找到可用输入向量的集合为空
			Builder.coveredPaths = new ArrayList<CFGPath>();
			
			Builder.allPaths = new ArrayList<CFGPath>();
			int pathsSize = Builder.uncheckedPaths.size();
			for (int i=0; i<pathsSize; i++)
			{
				Builder.allPaths.add(Builder.uncheckedPaths.get(i).clonePath());
			}
			
			while (!Builder.uncheckedPaths.isEmpty())
			{
				System.out.println("为第"+Builder.getPathNum(uncheckedPaths.get(0))+"条路径生成测试数据:");
				
				// 获取目标路径编号
				int pathIndex = getPathNum(Builder.uncheckedPaths.get(0));
				// 初始化目标路径
				targetPath = Builder.uncheckedPaths.get(0);
				
//				System.out.println("当前路径中需要的辅助变量个数" + targetPath.getNumOfAuxiliaryVars());
//				System.out.println("当前路径中路径分支的个数" + targetPath.getNumOfPathPredicates());
				
				// 执行ATG过程				
				isCovered = GA_for_ATG.generateTestData(pathIndex-1);
				
				if (isCovered > -1)
				{
					Builder.countOfCoveredPath++;
					Builder.strCoveredPath += isCovered + ",";
					Builder.coveredPaths.add(Builder.allPaths.get(getPathNum(targetPath)-1).clonePath());
				}
				else
				{
					Builder.uncoveredPaths.add(Builder.allPaths.get(getPathNum(targetPath)-1).clonePath());
				}
				// 移除已执行过ATG的路径
				Builder.uncheckedPaths.remove(0);
			}
			
			// 输出最终结果
			printResult();	
		}		
	}
	
	/**
	 * 输出最终结果
	 */
	private static void printResult()
	{
		int numOfPath = Builder.allPaths.size();
		for (int i=0; i<numOfPath; i++)
		{
			System.out.println("/----------测试路径 "+ (i+1) +"-----------/");
			Builder.consoleStream.println("/----------测试路径 "+ (i+1) +"-----------/");
			System.out.print(Builder.allPaths.get(i).getPath().get(0).getName());
			Builder.consoleStream.print(Builder.allPaths.get(i).getPath().get(0).getName());
			for (int j=1; j<Builder.allPaths.get(i).getPath().size(); j++)
			{
				System.out.print("->"+Builder.allPaths.get(i).getPath().get(j).getName());
				Builder.consoleStream.print("->"+Builder.allPaths.get(i).getPath().get(j).getName());
			}
			System.out.println();
			Builder.consoleStream.println();
			
			if (Builder.allPaths.get(i).isCovered())
			{
				System.out.println("找到可用输入：");
				Builder.consoleStream.println("找到可用输入：");
				for (int k=0; k<Builder.allPaths.get(i).getOptimalParams().length; k++)
				{
					System.out.print(Builder.allPaths.get(i).getOptimalParams()[k] + ", ");
					Builder.consoleStream.print(Builder.allPaths.get(i).getOptimalParams()[k] + ", ");
				}
				System.out.println();
				Builder.consoleStream.println();
				System.out.println();
				Builder.consoleStream.println();
			}
			else
			{
				System.out.println("未找到可用输入数据，最优一组预测输入：（" + Builder.allPaths.get(i).getNumOfCoveredNodes() + "," + Builder.allPaths.get(i).getEndCoveredNodeName() + "）");
				Builder.consoleStream.println("未找到可用输入数据，最优一组预测输入：（" + Builder.allPaths.get(i).getNumOfCoveredNodes() + "," + Builder.allPaths.get(i).getEndCoveredNodeName() + "）");
				for (int k=0; k<Builder.allPaths.get(i).getOptimalParams().length; k++)
				{
					System.out.print(Builder.allPaths.get(i).getOptimalParams()[k] + ", ");
					Builder.consoleStream.print(Builder.allPaths.get(i).getOptimalParams()[k] + ", ");
				}
				System.out.println();
				Builder.consoleStream.println();
				System.out.println();
				Builder.consoleStream.println();
			}
		}
		System.out.println("\n覆盖比例："+Builder.coveredPaths.size()+" / "+Builder.allPaths.size());
		Builder.consoleStream.println("\n覆盖比例："+Builder.coveredPaths.size()+" / "+Builder.allPaths.size());
		System.out.print("\n覆盖路径：");
		Builder.consoleStream.print("\n覆盖路径：");
		int pathIndex = 0;
		for (int i=0; i<Builder.coveredPaths.size(); i++)
		{
			pathIndex = getPathNum(Builder.coveredPaths.get(i));
			System.out.print(pathIndex + ",");
			Builder.consoleStream.print(pathIndex + ",");
		}
		System.out.println();
		Builder.consoleStream.println();
	}
	
	/**
	 * 获取路径编号（从1开始）
	 * @param paths
	 * @param path
	 * @return
	 */
	public static int getPathNum(CFGPath path)
	{
		int pathsSize = Builder.allPaths.size();
		for (int i=0; i<pathsSize; i++)
		{
			if (Builder.allPaths.get(i).isEqual(path.getPath()))
			{
				return (i+1);
			}
		}
		
		return -1;
	}
	
	/**
	 * 从CFG树的条件节点中取出逻辑符号，并记录到该条件节点中
	 * @param currentNode
	 */
	public void addSymbol(CFGNode currentNode){
		if (currentNode != null) {
			if (currentNode.getOffset() != -1 && !nodePrinted.contains(currentNode.getOffset())) {
				if (currentNode.getBinaryExpression() != null) {
					IASTBinaryExpression iabe = currentNode.getBinaryExpression();
					String operator = ASTSignatureUtil.getBinaryOperatorString(iabe);
					if(operator.compareTo(">")==0){
						if(currentNode.getIfChild() != null){
							currentNode.getIfChild().setOperator(">");
						}
						if(currentNode.getElseChild() != null){
							currentNode.getElseChild().setOperator("<=");
						}
					}else if(operator.compareTo(">=")==0){
						if(currentNode.getIfChild() != null){
							currentNode.getIfChild().setOperator(">=");
						}
						if(currentNode.getElseChild() != null){
							currentNode.getElseChild().setOperator("<");
						}
					}else if(operator.compareTo("<")==0){
						if(currentNode.getIfChild() != null){
							currentNode.getIfChild().setOperator("<");
						}
						if(currentNode.getElseChild() != null){
							currentNode.getElseChild().setOperator(">=");
						}
					}else if(operator.compareTo("<=")==0){
						if(currentNode.getIfChild() != null){
							currentNode.getIfChild().setOperator("<=");
						}
						if(currentNode.getElseChild() != null){
							currentNode.getElseChild().setOperator(">");
						}
					}else if(operator.compareTo("==")==0){
						if(currentNode.getIfChild() != null){
							currentNode.getIfChild().setOperator("==");
						}
						if(currentNode.getElseChild() != null){
							currentNode.getElseChild().setOperator("!=");
						}
					}else if(operator.compareTo("!=")==0){
						if(currentNode.getIfChild() != null){
							currentNode.getIfChild().setOperator("!=");
						}
						if(currentNode.getElseChild() != null){
							currentNode.getElseChild().setOperator("==");
						}
					}
				}
			}
			nodePrinted.add(currentNode.getOffset());
			List<CFGNode> children = currentNode.getChildren();
			if (children != null) {
				for (int i = 0; i < children.size(); i++) {
					if (!nodePrinted.contains(children.get(i).getOffset())) {
						addSymbol(children.get(i));
					}
				}
			}
			addSymbol(currentNode.getIfChild());
			addSymbol(currentNode.getElseChild());
		}
	}
	
	/**
	 * 插桩程序
	 * @param filePath
	 */
	private void instrumantation(String filePath)
	{
		Builder.nodePrinted = new ArrayList<Integer>();
		List<CFGNode> nodeList = new ArrayList<CFGNode>();
		this.getAllCFGNode(cfgStartNode, nodeList);
		this.sortCFGNodes(nodeList);
		int offsetIncreased = 0;
		int nodeListSize = nodeList.size();
		for (int i=0; i<nodeListSize; i++)
		{
			if (nodeList.get(i).getBinaryExpression() != null)
			{
				IASTBinaryExpression iabe = nodeList.get(i).getBinaryExpression();
				IASTNode[] iabeChildren = iabe.getChildren();
				if (iabeChildren.length == 2)
				{
					IASTExpression iaeLeft = (IASTExpression) iabeChildren[0];
					IASTExpression iaeRight = (IASTExpression) iabeChildren[1];
					String instrument = "printf(\"node%d %d %s if\\r\\n\",";
					if (nodeList.get(i).getSign() == 2)
					{
						instrument = "printf(\"node%d %d %s for\\r\\n\",";
					}					
					instrument += nodeList.get(i).getNodeNumber()
								  + ","
								  + "((" + iaeLeft.getRawSignature() + ")"
								  + "-"
								  + "(" + iaeRight.getRawSignature() + "))"
								  + ","
								  + "\"" + iabe.getRawSignature() + "\""
								  + "),";
					this.insertBeforeOffset(instrument, nodeList.get(i).getOffset()+offsetIncreased, filePath);
					offsetIncreased = offsetIncreased + instrument.length();
				}
			}
			else
			{
				String instrument = "printf(\"node%d\\r\\n\"," + nodeList.get(i).getNodeNumber() + ");\r\n\t";
				this.insertBeforeOffset(instrument, nodeList.get(i).getOffset()+offsetIncreased, filePath);
				offsetIncreased = offsetIncreased + instrument.length();
			}
		}
	}
	
	/**
	 * 获取所有的CFG节点（无序）
	 * 为程序插桩做准备
	 * @param node
	 * @param nodeList
	 */
	private void getAllCFGNode(CFGNode node, List<CFGNode> nodeList)
	{
		if (node != null)
		{
			if (node.getOffset() != -1 && !Builder.nodePrinted.contains(node.getOffset()))
			{
				CFGNode nodeTemp = new CFGNode();
				nodeTemp.setBinaryExpression(node.getBinaryExpression());
				nodeTemp.setNodeNumber(node.getNodeNumber());
				nodeTemp.setOffset(node.getOffset());
				nodeTemp.setSign(node.getSign());
				nodeList.add(nodeTemp);
			}
			Builder.nodePrinted.add(node.getOffset());
			
			//迭代
			List<CFGNode> children = node.getChildren();
			if (children != null)
			{
				for (int i=0; i<children.size(); i++)
				{
					if (!Builder.nodePrinted.contains(children.get(i).getOffset()))
					{
						this.getAllCFGNode(children.get(i), nodeList);
					}
				}
			}
			this.getAllCFGNode(node.getIfChild(), nodeList);
			this.getAllCFGNode(node.getElseChild(), nodeList);
		}
	}
	
	/**
	 * 对所有CFG节点按节点号从小到大排序
	 * @param nodeList
	 */
	private void sortCFGNodes(List<CFGNode> nodeList)
	{
		List<CFGNode> nodeListTemp = new ArrayList<CFGNode>();
		int nodeListSize = nodeList.size();
		for (int i=0; i<nodeListSize; i++)
		{
			nodeListTemp.add(null);
		}
		for (int i=0; i<nodeListSize; i++)
		{
			nodeListTemp.set(nodeList.get(i).getNodeNumber()-1, nodeList.get(i));
		}
		for (int i=0; i<nodeListSize; i++)
		{
			nodeList.set(i, nodeListTemp.get(i));
		}
	}
	
	/**
	 * 在CFG节点中插桩
	 * @param fragment
	 * @param offset
	 * @param filePath
	 */
	private void insertBeforeOffset(String fragment, int offset, String filePath)
	{
		try {
			RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
			int rafLength = (int)raf.length();
			byte[] bytes = new byte[rafLength-offset];
			raf.seek(offset);
			raf.read(bytes);

			//如果当前节点以{}包裹，则插入点调整到{后
			String str = new String(bytes);
			char[] charArray = str.toCharArray();
			int charTemp = -1;
			int numberOfBrace = 0;
			int i = 0;
			for (i=0; i<charArray.length; i++)
			{
				charTemp = (int)charArray[i];
				if (charTemp == 32 || charTemp == 10 || charTemp == 13 || charTemp == 9)
				{
					continue;
				}
				else if (charTemp == 123 && numberOfBrace <= 1)
				{
					numberOfBrace++;
					continue;
				}
				else
				{
					break;
				}
			}
			
			if (i > 0)
			{
				bytes = new byte[rafLength-offset-i];
				raf.seek(offset+i);
				raf.read(bytes);
				raf.seek(offset+i);
			}
			else
			{
				raf.seek(offset);
			}
			raf.write(fragment.getBytes());
			raf.write(bytes);
			raf.close();			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 设置CDT插件的遍历参数，并且遍历AST
	 * @param v
	 * @param iatu
	 */
	private void setParsed(ASTVisitor v, IASTTranslationUnit iatu) {
		v.shouldVisitStatements = true;
		v.includeInactiveNodes = true;
		v.shouldVisitAmbiguousNodes = true;
		v.shouldVisitArrayModifiers = true;
		v.shouldVisitBaseSpecifiers = true;
		v.shouldVisitDeclarations = true;
		v.shouldVisitDeclarators = true;
		v.shouldVisitDeclSpecifiers = true;
		v.shouldVisitDesignators = true;
		v.shouldVisitEnumerators = true;
		v.shouldVisitExpressions = true;
		v.shouldVisitImplicitNameAlternates = true;
		v.shouldVisitImplicitNames = true;
		v.shouldVisitInitializers = true;
		v.shouldVisitNames = true;
		v.shouldVisitNamespaces = true;
		v.shouldVisitParameterDeclarations = true;
		v.shouldVisitPointerOperators = true;
		v.shouldVisitProblems = true;
		v.shouldVisitTemplateParameters = true;
		v.shouldVisitTranslationUnit = true;
		v.shouldVisitTypeIds = true;
		iatu.accept(v);
	}

}
